<?php

namespace hunma\basic;

use hunma\exception\ApiException;
use Illuminate\Database\Eloquent\Builder;
use support\Db;
use support\Log;
use support\Model;

/**
 * 基础模型类
 * 提供通用的数据库操作方法
 */
class BaseModel extends Model
{
    /**
     * 构建查询构造器
     *
     * @param array $where 查询条件
     * @param array $order 排序规则
     * @param string|array $field 查询字段
     * @param string $group 分组字段
     * @return Builder 查询构造器实例
     */
    public static function buildQuery(array $where = [], array $order = [], $field = '*', string $group = ''): Builder
    {
        $query = self::query();

        if ($field !== '*') {
            if (is_array($field) && !empty($field)) {
                // 数组且非空，使用select
                $query->select($field);
            } elseif (is_string($field)) {
                // 字符串，使用selectRaw
                $query->selectRaw($field);
            }
            // 空数组时默认选择全部字段
        }

        // 处理where条件
        if (!empty($where)) {
            // 检测是否是关联数组 (一维键值对)
            $isAssoc = array_keys($where) !== range(0, count($where) - 1);

            if ($isAssoc) {
                // 处理关联数组格式: ['column' => 'value']
                $query->where($where);
            } else {
                // 处理标准格式: [['column', 'op', 'value'], function($q){...}, ...]
                foreach ($where as $condition) {
                    if ($condition instanceof \Closure) {
                        $query->where($condition);
                    } else {
                        $query->where([$condition]);
                    }
                }
            }
        }

        // 处理排序
        foreach ($order as $column => $direction) {
            $direction = strtolower($direction);
            if (!in_array($direction, ['asc', 'desc'])) {
                $direction = 'asc';
            }
            $query->orderBy($column, $direction);
        }

        // 处理分组
        if (!empty($group)) {
            $query->groupBy($group);
        }

        return $query;
    }

    /**
     * 验证ID是否存在且有效
     *
     * @param array $data 包含ID的数据数组
     * @throws \Exception 当ID为空或无效时
     */
    protected static function validateId(array $data): void
    {
        if (!isset($data['id']) || !$data['id']) {
            throw new \Exception("缺少有效的ID参数", 400);
        }

        if (!is_numeric($data['id']) || $data['id'] <= 0) {
            throw new \Exception("ID必须是正整数", 400);
        }
    }

    /**
     * 根据ID查找记录，不存在时抛出异常
     *
     * @param int $id 记录ID
     * @return self 查找到的模型实例
     * @throws \Exception 当记录未找到时
     */
    protected static function findOrFail(int $id): self
    {
        $model = self::find($id);
        if (!$model) {
            throw new \Exception("找不到ID为 {$id} 的记录", 400);
        }
        return $model;
    }

    //============================= 查询方法 =============================//

    /**
     * 分页获取数据列表
     *
     * @param array $where 查询条件
     * @param array $order 排序规则
     * @param string|array $field 查询字段
     * @param int $page 页码
     * @param int $limit 每页条数
     * @param string $group 分组字段
     * @param array $andWhere 额外的AND查询条件
     * @return array 查询结果数组
     */
    public static function getList(array $where = [], array $order = ['id' => 'DESC'], $field = '*', int $page = 1, int $limit = 10, string $group = '', array $andWhere = [],array $with=[]): array
    {
        $query = self::buildQuery($where, $order, $field, $group);

        if (!empty($andWhere)) {
            foreach ($andWhere as $key => $condition) {
                if (is_array($condition)) {
                    if (isset($condition[0]) && isset($condition[1])) {
                        // 处理数组格式 ['column', 'operator', 'value']
                        $column = $condition[0];
                        $operator = $condition[1];
                        $value = $condition[2] ?? null;

                        // 特殊处理 'in' 操作符
                        if (strtolower($operator) === 'in' && is_array($value)) {
                            $query->whereIn($column, $value);
                        }
                        // 也可以添加类似的处理 'not in'
                        else if (strtolower($operator) === 'not in' && is_array($value)) {
                            $query->whereNotIn($column, $value);
                        }
                        else {
                            $query->where($column, $operator, $value);
                        }
                    } else {
                        // 处理其他数组格式
                        $query->where(function($q) use ($condition) {
                            foreach ($condition as $subKey => $subValue) {
                                $q->where($subKey, $subValue);
                            }
                        });
                    }
                } else {
                    // 处理简单的键值对格式
                    $query->where($key, $condition);
                }
            }
        }

        // Laravel 分页
        $offset = ($page - 1) * $limit;
        return $query->with($with)->skip($offset)->take($limit)->get()->toArray();
    }

    /**
     * 获取所有数据
     *
     * @param array $where 查询条件
     * @param string|array $field 查询字段
     * @param array $order 排序规则
     * @param string $group 分组字段
     * @return array 查询结果数组
     */
    public static function allList(array $where = [], $field = '*', array $order = ['id' => 'DESC'], string $group = ''): array
    {
        $query = self::buildQuery($where, $order, $field, $group);
        return $query->get()->toArray();
    }

    /**
     * 获取单条数据
     *
     * @param array $where 查询条件
     * @param array $field 查询字段
     * @param array $order 排序规则
     * @return array|null 查询结果或null
     */
    public static function getOne(array $where, $field = '*', array $order = [],int $isToArray = 1)
    {
        // 标准化字段参数
        $field = (is_array($field) && empty($field)) ? '*' : $field;

        $query = self::buildQuery($where, $order, $field);
        $result = $query->first();

        // 如果需要转换为数组，且结果不为空，则转换
        if ($result) {
            return $isToArray ? $result->toArray() : $result;
        } else {
            return null;
        }

    }

    /**
     * 获取一行数据（支持更多查询选项）
     *
     * @param array $where 查询条件
     * @param string|array $field 查询字段（默认全部字段）
     * @param array $order 排序规则
     * @param string $group 分组字段
     * @param string $having Having条件
     * @return array|null 查询结果或null
     */
    public static function getRow(array $where, $field = '*', array $order = [], string $group = '', string $having = ''): ?array
    {
        $query = self::buildQuery($where, $order, $field, $group);

        if (!empty($having)) {
            $query->havingRaw($having);
        }

        $result = $query->first();
        return $result ? $result->toArray() : null;
    }

    /**
     * 获取记录总数
     *
     * @param array $where 查询条件
     * @param array $andWhere 额外的AND查询条件
     * @return int 记录总数
     */
    public static function getTotal(array $where = [], array $andWhere = []): int
    {
        $query = self::buildQuery($where);

        if (!empty($andWhere)) {
            foreach ($andWhere as $key => $condition) {
                if (is_array($condition)) {
                    if (isset($condition[0]) && isset($condition[1])) {
                        // Handle array format ['column', 'operator', 'value']
                        $column = $condition[0];
                        $operator = $condition[1];
                        $value = $condition[2] ?? null;
                        $query->where($column, $operator, $value);
                    } else {
                        // Handle other array formats
                        $query->where(function($q) use ($condition) {
                            foreach ($condition as $subKey => $subValue) {
                                $q->where($subKey, $subValue);
                            }
                        });
                    }
                } else {
                    // Handle simple key => value format
                    $query->where($key, $condition);
                }
            }
        }

        return $query->count();
    }

    /**
     * 计算指定字段的总和
     *
     * @param string $column 需要计算总和的字段名
     * @param array $where 查询条件
     * @param array $andWhere 额外的AND查询条件
     * @return float|int 返回总和结果
     */
    public static function getSum(string $column, array $where = [], array $andWhere = []): float|int
    {
        $query = self::buildQuery($where);

        if (!empty($andWhere)) {
            $query->where($andWhere);
        }

        $sum = $query->sum($column);

        // 确保返回数字而非NULL
        return $sum ?: 0;
    }

    //============================= 写入方法 =============================//

    /**
     * 新增数据
     *
     * @param array $data 新增数据
     * @return int|string 返回主键ID
     */
    public static function add(array $data): int|string
    {
        $now = date('Y-m-d H:i:s');
        $data['create_time'] = $data['create_time'] ?? $now;
        $data['update_time'] = $data['update_time'] ?? $now;
        return self::insertGetId($data);
    }

    //============================= 更新方法 =============================//

    /**
     * 批量插入数据
     *
     * @param array $dataList 需要插入的数据数组（二维数组）
     * @return int 成功插入的条数
     * @throws \Exception
     */
    public static function addBatch(array $dataList): int
    {
        if (empty($dataList)) {
            return 0;
        }

        // 自动补全时间字段
        $time = date('Y-m-d H:i:s');
        foreach ($dataList as &$item) {
            $item['create_time'] = $item['create_time'] ?? $time;
            $item['update_time'] = $item['update_time'] ?? $time;
        }

        try {
            return Db::transaction(function() use ($dataList) {
                // Laravel批量插入
                return self::insert($dataList) ? count($dataList) : 0;
            });
        } catch (\Exception $e) {
            Log::error("批量插入失败: " . $e->getMessage(), [
                'data' => $dataList,
                'trace' => $e->getTraceAsString()
            ]);
            throw new ApiException("批量插入失败: " . $e->getMessage(), 500);
        }
    }

    /**
     * 更新记录
     *
     * @param array $data 包含id和需要更新的数据
     * @param array|null $allowFields 允许更新的字段列表，null表示允许所有字段
     * @return bool 是否更新成功
     * @throws ApiException 当更新失败时
     */
    public static function edit(array $data, ?array $allowFields = null): bool
    {
        self::validateId($data);

        try {
            return Db::transaction(function () use ($data, $allowFields) {
                $model = self::findOrFail($data['id']);

                // 执行前置钩子
                if (method_exists(static::class, 'beforeEdit') && static::beforeEdit($model, $data) === false) {
                    return false;
                }

                // 过滤只允许更新的字段
                $updateData = $allowFields ? array_intersect_key($data, array_flip($allowFields)) : $data;

                // 自动添加更新时间
                $updateData['update_time'] = date('Y-m-d H:i:s');
//                Log::error("更新记录xxx: " ,$updateData);
                // 执行更新
                $result = $model->update($updateData);
//                Log::error("更新结果xxx: " ,[$result]);

                // 执行后置钩子
                if (method_exists(static::class, 'afterEdit')) {
                    static::afterEdit($model, $data, $result);
                }

                return $result;
            });
        } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $e) {
            throw new ApiException("记录 ID:{$data['id']} 不存在", 404);
        } catch (\Exception $e) {
            // 记录错误日志
            Log::error("更新记录失败: " . $e->getMessage(), [
                'data' => $data,
                'trace' => $e->getTraceAsString()
            ]);
            throw new ApiException("更新失败: " . $e->getMessage(), $e->getCode() ?: 500);
        }
    }

    /**
     * 批量更新记录
     *
     * @param array $data 要更新的数据 ['field' => 'value', ...]
     * @param array $where 查询条件
     * @param array $andWhere 附加的AND条件
     * @return int 返回更新影响的行数
     */
    public static function updateBatch(array $data, array $where = [], array $andWhere = []): int
    {
        if (empty($data)) {
            return 0;
        }

        try {
            return Db::transaction(function() use ($data, $where, $andWhere) {
                $query = self::buildQuery($where);

                if (!empty($andWhere)) {
                    $query->where($andWhere);
                }

                // 添加更新时间
                $data['update_time'] = date('Y-m-d H:i:s');

                return $query->update($data);
            });
        } catch (\Exception $e) {
            Log::error("批量更新失败: " . $e->getMessage(), [
                'data' => $data,
                'where' => $where,
                'trace' => $e->getTraceAsString()
            ]);
            throw new ApiException("批量更新失败: " . $e->getMessage(), 500);
        }
    }

    //============================= 删除方法 =============================//

    /**
     * 删除数据
     *
     * @param array $where 删除条件
     * @param string $type 删除类型：soft-软删除，hard-硬删除
     * @return bool
     */
    public static function del(array $where, string $type = 'soft'): bool
    {
        try {
            return Db::transaction(function() use ($where, $type) {
                $model = self::where($where)->first();
                if (!$model) {
                    return false;
                }

                if ($type === 'soft') {
                    return $model->delete();
                } else {
                    return $model->forceDelete();
                }
            });
        } catch (\Exception $e) {
            Log::error("删除记录失败: " . $e->getMessage(), [
                'where' => $where,
                'trace' => $e->getTraceAsString()
            ]);
            throw new ApiException("删除失败: " . $e->getMessage(), 500);
        }
    }

    /**
     * 批量删除
     *
     * @param array $ids 主键ID数组
     * @param string $type 删除类型：soft-软删除，hard-硬删除
     * @return bool
     */
    public static function delBatch(array $ids, string $type = 'soft'): bool
    {
        if (empty($ids)) {
            return false;
        }

        try {
            return Db::transaction(function() use ($ids, $type) {
                if ($type === 'soft') {
                    $models = self::whereIn('id', $ids)->get();
                    foreach ($models as $model) {
                        $model->delete();
                    }
                    return true;
                } else {
                    return self::whereIn('id', $ids)->forceDelete() > 0;
                }
            });
        } catch (\Exception $e) {
            Log::error("批量删除记录失败: " . $e->getMessage(), [
                'ids' => $ids,
                'trace' => $e->getTraceAsString()
            ]);
            throw new ApiException("批量删除失败: " . $e->getMessage(), 500);
        }
    }

    //============================= 辅助方法 =============================//

    /**
     * 构建模糊查询条件
     *
     * @param array $params 查询参数
     * @param array $fields 需要模糊查询的字段
     * @return array 构建的where条件
     */
    public static function buildLikeWhere(array $params, array $fields): array
    {
        $where = [];
        foreach ($fields as $field) {
            if (isset($params[$field]) && $params[$field] !== '' && $params[$field] !== null) {
                $where[] = [$field, 'like', "%{$params[$field]}%"];
            }
        }
        return $where;
    }

    /**
     * 构建精确查询条件
     *
     * @param array $params 查询参数
     * @param array $fields 需要精确查询的字段
     * @return array 构建的where条件
     */
    public static function buildEqWhere(array $params, array $fields): array
    {
        $where = [];
        foreach ($fields as $field) {
            if (isset($params[$field]) && $params[$field] !== '' && $params[$field] !== null) {
                $where[$field] = $params[$field];
            }
        }
        return $where;
    }

    /**
     * 构建日期范围查询条件
     *
     * @param array $params 查询参数
     * @param string $field 日期字段名
     * @param string $startKey 开始日期参数名
     * @param string $endKey 结束日期参数名
     * @return array 构建的where条件
     */
    public static function buildDateRangeWhere(array $params, string $field, string $startKey = 'start_time', string $endKey = 'end_time'): array
    {
        $where = [];

        if (!empty($params[$startKey])) {
            $where[] = [$field, '>=', $params[$startKey].' 00:00:00'];
        }

        if (!empty($params[$endKey])) {
            $where[] = [$field, '<=', $params[$endKey].' 23:59:59'];
        }

        return $where;
    }
}
